# !/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2021/11/3 13:59
# @Author  : zuodengbo
# @Email   : zuodb@amoydx.com
# @File    : function
# @Software: PyCharm
from user.models import *
from sanic.log import logger
from utils.const import RoleAndAuthorityStatus, UserStatus, RespCode, PermissionType, UserJobStatus


async def query_user_info(user_id):
    """
    Query user info function
    :param user_id:
    :return:
    """
    _init_data = dict()
    role_data = dict()
    job_data = dict()
    # 查找用户信息
    try:
        user = await User.filter(id=user_id).first()
        if not user:
            msg = "Query user failed!"
            logger.error(msg)
            return _init_data
        user_data = user.__json__()

        # 查找用户角色信息
        user_role = await get_user_role(user_id)
        if user_role:
            role = user_role.role
            role_data["role_name"] = role.name
            role_data["role_code"] = role.code

        # 查找用户工作信息
        my_job = await UserJobInfo.filter(user_id=user_id, status=UserJobStatus.mission). \
            values("job_num", company="company__name",
                   group_address="company__address",
                   department="department__name",
                   position="position__name",
                   position_level="level", job_status="status")
        if my_job:
            job_data = json_format(my_job[0], default=str)

        _init_data["user_info"] = user_data
        _init_data["role_info"] = role_data
        _init_data["job_info"] = job_data
    except Exception as e:
        msg = "Query failed: {} line: {}".format(e, e.__traceback__.tb_lineno)
        logger.error(msg)

    return _init_data


async def bind_user_role(username, role_name):
    """
    Bind user role function
    :param username:
    :param role_name:
    :return:
    """
    try:
        user = await User.filter(username=username, status=UserStatus.enabled).first()
        role = await Role.filter(name=role_name, status=RoleAndAuthorityStatus.available).first()
        if not user:
            msg = "Username: {} not exist".format(username)
            logger.warning(msg)
            return RespCode.USER_NOT_EXISTS, msg
        if not role:
            msg = "Role: {} not exist".format(role_name)
            logger.warning(msg)
            return RespCode.ROLE_NOT_EXISTS, msg
        user_role = await UserRole.filter(user=user, role=role, status=RoleAndAuthorityStatus.available)
        if user_role:
            msg = "Role:{} have been bound with user:{}".format(role_name, username)
            logger.warning(msg)
            return RespCode.USER_ROLE_ALREADY_BOUND, msg

        await UserRole.create(user=user, role=role)
        msg = "Bind user role success !"

        return RespCode.SUCCESS, msg

    except Exception as e:
        msg = "User band role failed: error:{}, line:{}".format(e, e.__traceback__.tb_lineno)
        logger.error(msg)
        return RespCode.UNKNOWN_FAILURE, msg


async def get_user_role(user_id):
    """
    查询用户角色
    :param user_id:
    :return:
    """
    user_role = await UserRole.filter(
        user_id=user_id, status=RoleAndAuthorityStatus.available).select_related("role").first()
    if not user_role:
        return None

    return user_role


async def get_user_role_permissions(role_id):
    """
    查询角色权限
    :param role_id:
    :return:
    """
    # role_perms = await RoleAuthority.filter(role=role_id,
    #                                         status=RoleAndAuthorityStatus.available).\
    #     select_related("authority").all()
    role_perms = await RoleAuthority.filter(role=role_id,
                                            status=RoleAndAuthorityStatus.available). \
        values(
        perm_id="authority_id",
        perm_name="authority__name",
        perm_code="authority__code",
        perm_path="authority__path",
        perm_uri="authority__uri",
        perm_type="authority__type",
        perm_icon="authority__icon",
        perm_parent="authority__parent_id",
        perm_status="authority__status"
    )
    catalogs = list()
    menus = list()
    buttons = list()
    for perm in role_perms:
        perm_dict = dict()
        child_dict = dict()
        button_dict = dict()
        perm_id = perm["perm_id"]
        perm_name = perm["perm_name"]
        perm_code = perm["perm_code"]
        perm_path = perm["perm_path"]
        perm_uri = perm["perm_uri"]
        perm_type = perm["perm_type"]
        perm_icon = perm["perm_icon"]
        perm_parent = perm["perm_parent"]
        perm_status = perm["perm_status"]
        if perm_status == RoleAndAuthorityStatus.frozen:
            continue
        if perm_type == PermissionType.catalog:
            perm_dict["id"] = perm_id
            perm_dict["menu"] = perm_name
            perm_dict["code"] = perm_code
            perm_dict["icon"] = perm_icon
            perm_dict["children"] = list()
            catalogs.append(perm_dict)
        elif perm_type == PermissionType.menu:
            catalogs_ids = list()
            for parent in catalogs:
                item_id = parent["id"]
                catalogs_ids.append(item_id)
                if perm_parent != item_id:
                    continue
                child_dict["id"] = str(item_id) + "-" + str(perm_id)
                child_dict["menu"] = perm_name
                child_dict["icon"] = perm_icon
                child_dict["path"] = perm_path
                child_dict["uri"] = perm_uri
                child_dict["code"] = perm_code
                menus.append(child_dict)
                parent["children"].append(child_dict)
            if perm_parent not in set(catalogs_ids):
                child_dict["id"] = perm_id
                child_dict["menu"] = perm_name
                child_dict["icon"] = perm_icon
                child_dict["path"] = perm_path
                child_dict["uri"] = perm_uri
                child_dict["code"] = perm_code
                catalogs.append(child_dict)

        elif perm_type == PermissionType.button:
            button_dict["id"] = perm_id
            button_dict["name"] = perm_name
            button_dict["code"] = perm_code
            button_dict["icon"] = perm_icon
            buttons.append(button_dict)

    return catalogs, menus, buttons


async def check_path(uri):
    """
    查询数据库是否存在该路径
    :param uri:
    :return:
    """
    perm = await Authority.filter(uri=uri).count()
    if perm > 0:
        return True
    else:
        return False


async def check_uri(uri, user_id):
    """
    校验访问权限
    :param user_id:
    :param uri:
    :return:
    """
    try:
        # 查询用户角色
        user_role = await get_user_role(user_id)
        if not user_role:
            return False, "Forbidden! You do not have permission to access"
        # 查询角色权限
        catalogs, path_menus, buttons = await get_user_role_permissions(user_role)
        if not path_menus:
            return False, "Forbidden! You do not have permission to access"

        is_allowed = False
        # 检查是否属于角色权限内
        for menu in path_menus:
            if uri != menu["uri"]:
                continue
            else:
                is_allowed = True

        if not is_allowed:
            # 检查数据库是否存在该路径权限配置
            perm = await check_path(uri)
            if not perm:
                is_allowed = True

        if not is_allowed:
            return is_allowed, "Forbidden! You do not have permission to access"

        return is_allowed, "Allowed Access"

    except Exception as e:
        msg = "Other failure occurred when query permissions: error: {}, line:{}". \
            format(e, e.__traceback__.tb_lineno)
        logger.warning(msg)
        return False, msg


async def roles_or_perms(model, user_id=None, role_code=None, role_id=None):
    """
    根据model返回具体有效的数据
    :param role_id:
    :param role_code: 超级管理员返回所有角色和权限，其它则返回其拥有的角色和权限
    :param user_id:
    :param model:
    :return:
    """
    role_data = list()
    perm_data = list()

    try:
        if model == "Role":
            if role_code != "SuperAdmin":
                roles = await Role.filter(
                    status=RoleAndAuthorityStatus.available, creator=user_id).all().order_by("id")
            else:
                roles = await Role.filter(status=RoleAndAuthorityStatus.available).all().order_by("id")
            for role in roles:
                _role_dict = dict()
                _role_dict["id"] = role.__json__().get("id")
                _role_dict["name"] = role.__json__().get("name")
                role_data.append(_role_dict)

            return RespCode.SUCCESS, role_data

        elif model == "Authority":
            if role_code != "SuperAdmin":
                role_perms = await RoleAuthority.filter(
                    role__code=role_code, status=RoleAndAuthorityStatus.available).\
                    select_related("authority").all().order_by("id")
                perms = []
                for item in role_perms:
                    perms.append(item.authority)
            else:
                perms = await Authority.filter(status=RoleAndAuthorityStatus.available).all().order_by("id")
            catalogs = list()
            buttons = list()
            for perm in perms:
                perm_dict = dict()
                child_dict = dict()
                button_dict = dict()
                perm_id = perm.__json__().get("id")
                perm_name = perm.__json__().get("name")
                perm_type = perm.__json__().get("type")
                perm_parent = perm.__json__().get("parent_id")
                if perm_type == PermissionType.catalog:
                    perm_dict["id"] = perm_id
                    perm_dict["name"] = perm_name
                    perm_dict["children"] = list()
                    catalogs.append(perm_dict)
                elif perm_type == PermissionType.menu:
                    catalogs_ids = list()
                    for parent in catalogs:
                        item_id = parent["id"]
                        catalogs_ids.append(item_id)
                        if perm_parent != item_id:
                            continue
                        child_dict["id"] = perm_id
                        child_dict["name"] = perm_name
                        parent["children"].append(child_dict)
                    if perm_parent not in set(catalogs_ids):
                        child_dict["id"] = perm_id
                        child_dict["name"] = perm_name
                        catalogs.append(child_dict)

                elif perm_type == PermissionType.button:
                    button_dict["id"] = perm_id
                    button_dict["name"] = perm_name
                    buttons.append(button_dict)
            perm_data.extend(catalogs)
            perm_data.extend(buttons)

            return RespCode.SUCCESS, perm_data

        elif model == "RolePerms" and role_id:
            role_perms = await RoleAuthority.filter(role_id=role_id).values_list("authority_id")
            if not role_perms:
                role_perms = []
            return RespCode.SUCCESS, role_perms

        else:
            msg = "Model must be Role or Authority"
            return RespCode.UNPROCESSABLE_ENTITY, msg

    except Exception as e:
        msg = "Query {} Data failed: {}, line: {}".format(model, e, e.__traceback__.tb_lineno)
        logger.error(msg)
        return RespCode.UNKNOWN_FAILURE, msg
